import { IConcurrentUtils } from '../interface/IConcurrentUtils';

/**
 * 并发执行任务
 */
class ConcurrentUtils implements IConcurrentUtils {
  constructor() {}

  public parallelTask(
    tasks: Function[],
    parallelCount: number
  ): Promise<string> {
    return new Promise((resolve): void => {
      if (tasks.length === 0) {
        resolve('任务执行完毕，执行数量0');
        return;
      }

      let nextIndex: number = 0; // 下一个任务的下标
      let finishTaskCount: number = 0; // 执行任务数量

      // 运行下一个任务
      function _run(): void {
        const task = tasks[nextIndex];
        nextIndex++;

        task().then((): void => {
          finishTaskCount++;
          if (nextIndex < tasks.length) {
            _run();
          } else if (finishTaskCount === tasks.length) {
            resolve(`任务完成，完成数量${finishTaskCount}`);
          }
        });
      }

      for (let i: number = 0; i < tasks.length && i < parallelCount; i++) {
        _run();
      }
    });
  }

  public parallelTaskV1(
    tasks: Function[],
    parallelCount: number
  ): Promise<{ data: any[]; msg: string }> {
    return new Promise<{ data: any[]; msg: string }>((resolve): void => {
      if (tasks.length === 0) {
        resolve({
          data: [],
          msg: `任务完成，完成数量0`,
        });
        return;
      }

      let nextIndex: number = 0; // 下一个任务的下标
      let finishTaskCount: number = 0; // 执行任务数量
      const result: any = []; // 结果

      async function _run(): Promise<void> {
        const i = nextIndex;
        const task = tasks[i];
        nextIndex++;
        try {
          result[i] = await task();
        } catch (error) {
          result[i] = error;
        } finally {
          finishTaskCount++;
          if (finishTaskCount === tasks.length)
            resolve({
              data: result,
              msg: `任务完成，完成数量${finishTaskCount}`,
            });
          if (nextIndex < tasks.length) {
            await _run();
          }
        }
      }

      for (let i: number = 0; i < Math.min(tasks.length, parallelCount); i++) {
        _run();
      }
    });
  }
}

const concurrentUtils = new ConcurrentUtils();
export default concurrentUtils;
